#include "exchange_step.h"
#include "common/system_log.h"
#include "ExchangeObject.h"
#include "utils/file_util.h"
#include <cstddef>
#include <memory>
#include <unordered_map>
#include "common/IndexSet.hpp"
#include "topology/TopoExplorerTool.hpp"
#include "ExchangeParameter.h"
#include "utils/string_util.h"
#include "ExchangeTopoShape.h"
#include "ExchangeMaterials.h"

AMCAX_EXCHANGE_NAMESPACE_BEGIN

void ExchangeStep::ReadFromFilePath() 
{
    #ifdef AMCAX_STEP_ENABLE
    if (!AMCAX::Exchange::file_util::is_file_exist(filePath_)) {
        LOG_ERROR("file not exist:{}",filePath_) ;
        return ;
    }
    AMCAX::STEP::StepReader reader(filePath_) ;
    AMCAX::STEP::StepDataList& step_shape_list = reader.GetShapes();
    read_common(reader) ;
    #endif
}

void ExchangeStep::ReadFromFileStream()
{
    #ifdef AMCAX_STEP_ENABLE
    if(nullptr == inFileStream_) {
        LOG_ERROR("inFileStream_ is null") ;
        return  ;
    }
    AMCAX::STEP::StepReader reader(*inFileStream_) ;
    AMCAX::STEP::StepDataList& step_shape_list = reader.GetShapes();
    read_common(reader) ;
    #endif
}

#ifdef AMCAX_STEP_ENABLE
void ExchangeStep::read_common(AMCAX::STEP::StepReader &reader) 
{
    reader.Read() ;
    const AMCAX::STEP::StepDataList& step_shape_list = reader.GetShapes();
    for (const auto &root_step_shape : step_shape_list) 
    {
        // NOTE: 如果当前shape没有子节点且没有子shape，则跳过
        if (root_step_shape->Shapes().size() == 0 && root_step_shape->Children().size() == 0)
            continue;
        // NOTE: 如果当前shape有子节点，则遍历子节点,并将返回的节点添加到root_exchange_object的child中
        auto exchange_object = read_common_recursion(root_step_shape);
        exchangeObjectList_.push_back(exchange_object);
    }
}
#endif

bool IsGBK(const std::string& data) 
{
    int state = 0;
    for (size_t i = 0; i < data.size (); ++i) {
        if (data[i] & 0x80) {
            state = 1 - state;
        } else if (state == 1) {
            return false;
        }
    }
    return true;
}
#ifdef AMCAX_STEP_ENABLE
// NOTE: 递归遍历step data数据，将step数据转换为ExchangeObject对象
std::shared_ptr<ExchangeObject> ExchangeStep::read_common_recursion(std::shared_ptr<AMCAX::STEP::StepData> pData)
{
    std::vector<std::shared_ptr<ExchangeObject>> objs;

    AMCAX::TopoLocation loc = pData->Transformation();
    std::unordered_map<AMCAX::TopoShape, AMCAX::STEP::ShapeRGBA>& colorMap = pData->PartColor();
    std::unordered_map<AMCAX::TopoShape, std::string>& names = pData->PartName();
    const std::vector<AMCAX::STEP::StepPropertySet>& props = pData->Properties();

    for (size_t shapeIdx = 0; shapeIdx < pData->Shapes().size(); ++shapeIdx)
    {
        auto shape = pData->Shapes()[shapeIdx];
        shape.SetLocation(loc);

        std::string thisName = names.count(shape) > 0 ? names[shape] : pData->Name();
        if (IsGBK(thisName)) thisName = "shape";

        if (shape.Type() == AMCAX::ShapeType::Compound && shapeIdx < props.size() && props[shapeIdx].Size() > 0) 
        {
            auto& thisProp = props[shapeIdx];

            // 所有子项
            AMCAX::IndexSet<AMCAX::TopoShape> allSet;
            AMCAX::TopoExplorerTool::MapShapes(shape, allSet);
            
            // 仅下级
            std::vector<AMCAX::TopoShape> subSet;
            for (AMCAX::TopoIterator it(shape); it.More(); it.Next()) subSet.push_back(it.Value());

            for(auto& subShape : subSet) 
            {
                int subIndex = allSet.index(subShape);
                const auto& propUnit = thisProp.GetProperty(subIndex);

                std::string subName = propUnit.NameHasValue() ? propUnit.Name() : thisName;
                if (IsGBK(subName)) subName = "shape";

                auto dObj = std::make_shared<ExchangeObject>(string_util::generate_UUID());
                dObj->name = subName;
                objs.push_back(dObj);
                dObj->shape = std::make_shared<ExchangeTopoShape>(subShape);

                AMCAX::STEP::ShapeRGBA stepColor = propUnit.Color();
                if (stepColor.IsValidRGB() || stepColor.IsValidA())
                {
                    std::string rgb;
                    double transparency;
                    ExchangeMaterialItem::SplitRgba(stepColor.to_string(), rgb, transparency);
                    if (!stepColor.IsValidRGB()) rgb = "#d2d2ff";
                    if (!stepColor.IsValidA()) transparency = 0.;

                    ExchangeMaterialItem mat(rgb);
                    // LOG_DEBUG("test ExchangeMaterial rgb: {} transparency: {}", rgb, transparency);
                    mat.transparency = transparency;
                    if (!dObj->materials) {
                        dObj->materials = std::make_shared<ExchangeMaterials>() ;
                    }
                    dObj->materials->AddMaterial(subShape,mat) ;
                    // dObj->materials[subShape] = mat;
                }
            }
        }
        else 
        {
            auto dObj = std::make_shared<ExchangeObject>(string_util::generate_UUID());
            dObj->name = thisName;
            objs.push_back(dObj);
            dObj->shape = std::make_shared<ExchangeTopoShape>(shape);
            dObj->shape->GetShape().SetLocation(loc);

            for (auto& [sub, stepColor] : colorMap) 
            {
                if (stepColor.IsValidRGB() || stepColor.IsValidA())
                {
                    std::string rgb;
                    double transparency;
                    ExchangeMaterialItem::SplitRgba(stepColor.to_string(), rgb, transparency);
                    if (!stepColor.IsValidRGB()) rgb = "#d2d2ff";
                    if (!stepColor.IsValidA()) transparency = 0.;

                    ExchangeMaterialItem mat(rgb);
                    mat.transparency = transparency;
                    if(!dObj->materials){
                        dObj->materials = std::make_shared<ExchangeMaterials>() ;
                    }
                    // dObj->materials[sub] = mat;
                    dObj->materials->AddMaterial(sub,mat) ;
                }
            }
        }
    }

    // 单模型节点
    if (objs.size() == 1 && pData->Children().size() == 0) {
        return objs[0];
    }

    // 树状节点
    auto rootObj = std::make_shared<ExchangeObject>(string_util::generate_UUID());
    rootObj->name = pData->Name();
    for(auto& obj : objs) rootObj->AddChild(obj);
    if (pData->Children().size() > 0) {
        for(auto& ch : pData->Children()) {
            rootObj->AddChild(read_common_recursion(ch));
        }
    }
    return rootObj;
}

// NOTE: 将所有的模型合并成一个模型，然后再导出。 这样操作会丢失模型的颜色信息
// void ExchangeStep::write_one(const std::vector<std::shared_ptr<ExchangeObject>>& exobjs,std::shared_ptr<AMCAX::STEP::StepWriter> work_ptr)
void ExchangeStep::model_fusion_2_one_and_write(const std::vector<std::shared_ptr<ExchangeObject>>& raw_exchange_objects,std::shared_ptr<AMCAX::STEP::StepWriter> step_writer_ptr) 
{
    // NOTE: 模型融合过程需要先将嵌套的模型展开
    std::vector<std::shared_ptr<ExchangeObject>> flat_objects;
    NestedExchangeShapeToFlat(flat_objects, raw_exchange_objects);
    // for(auto& exObj : exobjs) {
    //     NestedExchangeShapeToFlat(flatObjs, exObj);
    // }

    AMCAX::STEP::StepOptions opt;
    opt.WriterMergeProductTrees = false;
    step_writer_ptr->SetOptions(opt);
    // NOTE: 将所有的模型合并成一个模型
    AMCAX::TopoBuilder builder;
    AMCAX::TopoCompound comp;
    builder.MakeCompound(comp);
    for (auto exobj : flat_objects) 
    {
        if (exobj->shape->GetShape().IsNull()) 
        {
            LOG_DEBUG("shape<{}> is null", exobj->name);
            continue;
        }
        builder.Add(comp, exobj->shape->GetShape());
    }
    // NOTE: 造融合后的shape
    std::shared_ptr<AMCAX::STEP::StepData> step_data(new AMCAX::STEP::StepData(comp, "AMCAX-MODEL"));
    step_writer_ptr->WriteShape(step_data);
    step_writer_ptr->Done() ;
}

// 导出所有非空节点
// void ExchangeStep::write_features(const std::vector<std::shared_ptr<ExchangeObject>>& exobjs,std::shared_ptr<AMCAX::STEP::StepWriter> work_ptr)
// NOTE: 提取所有的非空节点，并写到文件里
void ExchangeStep::extract_all_non_node_and_write(const std::vector<std::shared_ptr<ExchangeObject>>& exchange_objects,std::shared_ptr<AMCAX::STEP::StepWriter> step_writer_ptr)
{
    // NOTE: 先将模型展开并放入到vector中
    std::vector<std::shared_ptr<ExchangeObject>> flat_exchange_objects;
    NestedExchangeShapeToFlat(flat_exchange_objects, exchange_objects);
    // for(auto& exobj : exobjs) {
    //     NestedExchangeShapeToFlat(flatObjs, exobj);
    // }

    AMCAX::STEP::StepOptions opt;
    opt.WriterMergeProductTrees = false;
    step_writer_ptr->SetOptions(opt);

    for(auto& exchange_object : flat_exchange_objects)
    {
        // NOTE: 如果shape为空，则跳过
        if (exchange_object->shape->GetShape().IsNull()) continue;

        std::shared_ptr<AMCAX::STEP::StepData> step_data(new AMCAX::STEP::StepData(exchange_object->shape->GetShape(), exchange_object->name));

        std::unordered_map<AMCAX::TopoShape, AMCAX::STEP::ShapeRGBA>& colors = step_data->PartColor();
        if(exchange_object->materials)
        for(auto& [sub, color] : exchange_object->materials->GetMaterials()) {
            colors.insert(std::make_pair(sub, AMCAX::STEP::ShapeRGBA::MakeColor(color.GetColor())));
        }

        step_writer_ptr->WriteShape(step_data);
    }

    step_writer_ptr->Done() ;
}

// NOTE: 写所有的shape到文件中
void ExchangeStep::write_all(const std::vector<std::shared_ptr<ExchangeObject>>& exchange_objects,std::shared_ptr<AMCAX::STEP::StepWriter> step_writer_ptr)
{
    AMCAX::STEP::StepOptions opt;
    opt.WriterMergeProductTrees = false;
    step_writer_ptr->SetOptions(opt);

    AMCAX::STEP::StepDataList step_tree;
    
    for(auto& root_exchange_object : exchange_objects) 
    {
        std::shared_ptr<AMCAX::STEP::StepData> step_data(new AMCAX::STEP::StepData());
        step_data->Name() = root_exchange_object->name;
        step_tree.push_back(step_data);

        if (root_exchange_object->shape->GetShape().IsNull()) {
            for(auto& subobj : root_exchange_object->childs) write_all_recursion(subobj, step_data);
        } else {
            step_data->Name() = "AMCAX-Writer";
            write_all_recursion(root_exchange_object, step_data);
        }
    }

    step_writer_ptr->WriteShapes(step_tree);
    step_writer_ptr->Done() ;
}

// NOTE: 递归遍历当前的ExchangeObject，并将所有的非空节点对象写入到root中
void ExchangeStep::write_all_recursion(std::shared_ptr<ExchangeObject> exchange_object, std::shared_ptr<AMCAX::STEP::StepData> root)
{
    std::shared_ptr<AMCAX::STEP::StepData> step_data(new AMCAX::STEP::StepData());
    step_data->Name() = exchange_object->name;
    root->Children().push_back(step_data);

    if (!exchange_object->shape->GetShape().IsNull()) 
    {
        step_data->Shapes().push_back(exchange_object->shape->GetShape());

        std::unordered_map<AMCAX::TopoShape, AMCAX::STEP::ShapeRGBA>& colors = step_data->PartColor();
        
        if(exchange_object->materials)
        for(auto& [sub, color] : exchange_object->materials->GetMaterials()) {
            LOG_INFO("sub:{}, color:{}",exchange_object->name, color.GetColor()) ;
            colors.insert(std::make_pair(sub, AMCAX::STEP::ShapeRGBA::MakeColor(color.GetColor())));
        }
    }

    for(auto& subobj : exchange_object->childs) write_all_recursion(subobj, step_data);
}
#endif

void ExchangeStep::Write(const std::vector<std::shared_ptr<ExchangeObject>>& exchange_shapes, const std::shared_ptr<ExchangeDocumentInstance> &exchangeDocumentInstance)
{
    #ifdef AMCAX_STEP_ENABLE
    std::shared_ptr<AMCAX::STEP::StepWriter> step_writer_ptr ;
    if (OUTPUT_FILE_TYPE::FILE_PATH == outputFileType_) {
        if (AMCAX::Exchange::file_util::is_file_exist(filePath_))
        {
            LOG_ERROR("file<{}> already exist",filePath_) ;
            return ;
        }
        step_writer_ptr.reset(new AMCAX::STEP::StepWriter(filePath_)) ;
    }
    else if (OUTPUT_FILE_TYPE::FILE_OUTPUT_STREAM == outputFileType_) 
    {
        if(nullptr == outFileStream_) {
            LOG_ERROR("outFileStream_ is null") ;
            return  ;
        }
        step_writer_ptr.reset(new AMCAX::STEP::StepWriter(*outFileStream_)) ;
    }else {
        LOG_ERROR("output file type:{} is unknown",(int)outputFileType_) ;
        return ;
    }

    StepOutputParameter *parameter = dynamic_cast<StepOutputParameter *>(GetExchangeParameter()) ;
    STEP_WRITE_TYPE writeType = STEP_WRITE_TYPE::WRITE_ALL ;
    if (nullptr != parameter) {
        writeType = parameter->GetWriteType() ;
    }

    switch (writeType) {
        case STEP_WRITE_TYPE::WRITE_ALL:
            write_all(exchange_shapes, step_writer_ptr) ;
        break;
        case STEP_WRITE_TYPE::WRITE_ALL_NON_EMPTY:
            extract_all_non_node_and_write(exchange_shapes, step_writer_ptr) ;
        break;
        case STEP_WRITE_TYPE::WRITE_ALL_2_ONE:
            model_fusion_2_one_and_write(exchange_shapes, step_writer_ptr) ;
        break;
        default:
            LOG_ERROR("invalid write type") ;
        break;
    }
    #endif
}


AMCAX_EXCHANGE_NAMESPACE_END